import java.util.*;
import java.io.*;
//import java.nio.*;

/**
 * 
 * @author Michael Cotterell
 * @author Paul Duke
 */
public class FileList<K extends Serializable> extends AbstractList<K> implements List<K> {

	/**
	 * The file in which the list is stored in.
	 */
	private File file = null;
	
	/**
	 * The size of the list.
	 */
	private int size = 0;
	
	/**
	 * IO Streams
	 */
	private FileOutputStream fos;
	private ObjectOutputStream oos;
	private FileInputStream fis;
	private ObjectInputStream ois;
	
	/**
	 * Initializes the IO Streams
	 * @throws IOException
	 */
	private void initializeStreams() throws IOException {
		this.fos = new FileOutputStream(this.file);
		this.oos = new ObjectOutputStream(this.fos);
		this.oos.writeObject(new Comparable [] {});
	}
	
	/**
	 * Resets the InputStreams
	 * @throws IOException
	 */
	private void resetInputStreams() throws IOException {
		if (this.fis != null) this.fis.close();
		if (this.ois != null) this.ois.close();
		this.fis = new FileInputStream(this.file);
		this.ois = new ObjectInputStream(this.fis);
	}
	
	/**
	 * Constructs a FileList that's stored in a temporary file.
	 * @throws IOException
	 */
	public FileList() throws IOException {
		this.file = File.createTempFile(this.getClass().getName(), "." + this.hashCode());
		this.file.deleteOnExit();
		this.initializeStreams();
	}
	
	/**
	 * Constructs a FileList that's stored in filename.
	 * @param filename
	 * @throws IOException 
	 */
	public FileList(String filename) throws NullPointerException, IOException {
		this.file = new File(filename);
		this.file.deleteOnExit();
		this.initializeStreams();
	}
	
	/**
	 * Constructs a FileList that's stored in file.
	 * @param file
	 * @throws IOException 
	 */
	public FileList(File file) throws IOException {
		this.file = file;
		this.file.deleteOnExit();
		this.initializeStreams();
	}
	
	@Override
	public K get(int index) throws IndexOutOfBoundsException {
		
		/**
		 * Perform bounds check
		 */
		if (index < 0 || index >= this.size()) {
			throw new IndexOutOfBoundsException();
		}
		
		/**
		 * Call the seek method
		 */
		return this.seek(index);
	
	}

	@Override
	public boolean add(K item) {
		
		try {
			
			/**
			 * Write the object to the file
			 */
			this.oos.writeObject(item);
			
			/**
			 * Increment the list size
			 */
			this.size++;
		
		} catch (Exception e) {
		
			e.printStackTrace();
			return false;
		
		}
		
		return true;
	}
	
	@Override
	public int size() {
		return this.size;
	}
	
	@Override
	public K set(int index, K item) {
		// TODO Auto-generated method stub
		return item;
	}
	
	@SuppressWarnings("unchecked")
	private K seek(int index) {
		
		K item = null;
		
		try {
			
			this.resetInputStreams();
			
			this.ois.readObject();
			
			for (int i = 0; i <= index; i++) {
				item = (K) this.ois.readObject();
			}
			
		} catch (EOFException eof) {
			
		
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return item;
	}
	
	/*************************************************************************************
     * The main method is used for testing purposes only.
     * @param args  the command-line arguments
     * @author Michael Cotterell
     */
    public static void main (String [] args)
    {
    	
    	try {
			
    		List<Comparable<?> []> list = new FileList<Comparable<?> []>("test.txt");
			
    		list.add(new Comparable[] {1, "Michael", 22, "mc"});
        	list.add(new Comparable[] {2, "Paul", 21, "pd"});
        	list.add(new Comparable[] {3, "Mitch", 19, "mf"});

			for (Comparable<?> [] tup : list) {
				for (Comparable<?> c : tup) {
					System.out.print(c + ":" + c.getClass().getSimpleName() + " ");
				}
				System.out.println();
			}
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    }
	
}
